package org.yq.wsclient;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity; 
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * WebService通用客户端
 * @author YQ
 *
 */
public class WSClient {
	private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd*HH:mm:ss");
	private String charset = "utf-8";
	private SoapBean soapBean;
	public WSClient(){
	}
	
	public WSClient(SoapBean soap){
		this.soapBean =soap;
	}
	
	/**
	 * 执行webservice
	 * @return
	 * @throws Exception
	 */
	public String execute() throws Exception{
		String rs = this.exec();
		return buildValue(rs).toString();
	}
	/**
	 * 执行返回对象的webservice
	 * @return
	 * @throws Exception
	 */
	public <T> T execute(Class<T> clz) throws Exception{
		T obj = null;
		String soapResponseData = this.exec();
		obj = this.buildValueObject(soapResponseData,clz);
		return obj;
	} 
	
	private String exec() throws Exception{
		String soapRequestData = this.buildParamStr();
		PostMethod postMethod = new PostMethod(this.getSoapBean().getUrl());
		byte[] b = soapRequestData.getBytes(this.charset);
		InputStream is = new ByteArrayInputStream(b, 0, b.length);
		RequestEntity re = new InputStreamRequestEntity(is, b.length,"text/xml");//soap11
		//RequestEntity re = new InputStreamRequestEntity(is, b.length,"application/soap+xml; charset=utf-8");//soap12
		postMethod.setRequestEntity(re);
		HttpClient httpClient = new HttpClient();
		int statusCode = httpClient.executeMethod(postMethod);
		if (statusCode == 200) {
			String soapResponseData = postMethod.getResponseBodyAsString();
			return soapResponseData;
		} else {
			throw new RuntimeException("调用失败！错误码：" + statusCode);
		}
	}

	/**
	 * 构建参数
	 * 
	 * @return
	 */
	private String buildParamStr() { 
		
		StringBuffer sb = new StringBuffer();
		sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
		sb.append("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
		sb.append("xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" ");
		sb.append("xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");//:soap12 " xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">"
		sb.append("<soap:Body xmlns:m=\"" + this.getSoapBean().getNamespace() + "\">");
		sb.append("<m:" + this.getSoapBean().getMethod()  + " >");
		if(this.getSoapBean().getParam() != null){
			
			@SuppressWarnings("unchecked")
			Iterator<String> keys = this.getSoapBean().getParam().keySet().iterator();
			while (keys.hasNext()) {
				String key = keys.next().toString();
				String val = "";
				if (this.getSoapBean().getParam().get(key) != null
						&& this.getSoapBean().getParam().get(key) instanceof Date) {
					Date d = (Date) this.getSoapBean().getParam().get(key);
					val = format.format(d);
					val = val.replace("*", "T");// 日期格式字符为：2015-07-27T10:05:52+08:00
				} else {
					val = this.getSoapBean().getParam().get(key) == null ? "" : this.getSoapBean().getParam()
							.get(key).toString();
				}
				sb.append("<" + key + " >");
				sb.append(val);
				sb.append("</" + key + " >");
			}
		}
		sb.append("</m:" + this.getSoapBean().getMethod() + ">");
		sb.append("</soap:Body>");
		sb.append("</soap:Envelope>");
		return sb.toString();
	}

	/**
	 * 构建返回数据
	 * 
	 * @param rs
	 * @return
	 */
	private String buildValue(String rs) throws Exception {
		String obj = null;
		NodeList lstNode = selectReturn(rs);
		if(lstNode != null && lstNode.getLength() > 0 ){
			Node node= lstNode.item(0);
			obj = node.getFirstChild().getNodeValue();
		}
		return obj;
	}
	
	/**
	 * 构建对象返回数据
	 * 
	 * @param rs
	 * @return
	 */
	private <T> T  buildValueObject(String rs,Class<T> cls) throws Exception {

		T obj = cls.newInstance();
		NodeList lstNode = selectReturn(rs);
		if(lstNode != null && lstNode.getLength() > 0 ){
			Node node= lstNode.item(0);
			NodeList lstNodeChild = (NodeList)node;
			for(int i = 0; i < lstNodeChild.getLength(); i++){
				Node nodeC= lstNodeChild.item(i);
				String _val = nodeC.getFirstChild().getNodeValue();
				Field field = cls.getDeclaredField(nodeC.getNodeName() );
				field.setAccessible(true);
				FieldValueFactory.setFieldValue(field, obj, _val);
			}
		}
		return obj;
	}
	
	private NodeList selectReturn(String rs) throws Exception{
		StringReader read = new StringReader(rs);
		InputSource source = new InputSource(read);
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document document = db.parse(source);
		NodeList lstNode = document.getElementsByTagName("return");
		return lstNode;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public SoapBean getSoapBean() {
		return soapBean;
	}

	public void setSoapBean(SoapBean soapTool) {
		this.soapBean = soapTool;
	}
	
	
}
